using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
using UnityEngine.Events;
/// <summary>
/// AsesstBudle管理
/// </summary>
public class ABManager : SingletonMono<ABManager>
{
    /// <summary>
    /// 资源包对象池
    /// 可优化的点
    /// key是否有必要做成枚举类型，更方便调用
    /// </summary>
    private Dictionary<string, AssetBundle> ABDic = new Dictionary<string, AssetBundle>();
    /// <summary>
    /// 清单文件所在的主ab包
    /// </summary>
    public AssetBundle mainFile = null;
    /// <summary>
    /// 清单文件（内部为所有ab包所依赖的其他包信息）
    /// </summary>
    public AssetBundleManifest manifestFile = null;

    

    /// <summary>
    /// 主ab包名
    /// 根据平台不同，主包名可能会不同
    /// </summary>
    private string MainABName
    {
        get
        {
#if UNITY_EDITOR
            return  "AB";
#elif UNITY_ANDROID
            return  "AB";
#else
            return  "AB";
#endif
        }
    }

    public bool isExist(string abfileKey)
    {
        if (!Instance.ABDic.ContainsKey(abfileKey))
            return false;
        return true;
        
    }
    public void AddToABDictionary(string abfileKey,AssetBundle ab)
    {
         if (!Instance.ABDic.ContainsKey(abfileKey))
            Instance.ABDic.Add(abfileKey, ab);
    }
    //在编辑器下是流目录，在手机上因为需要读写则是持久目录
   public string GetABPath(string fileName)
    {
#if UNITY_IOS && !UNITY_EDITOR
        return Application.persistentDataPath + "/AB/" + fileName;
#elif UNITY_ANDROID && !UNITY_EDITOR
        return Application.persistentDataPath + "/AB/" + fileName;
#elif UNITY_WEBGL && !UNITY_EDITOR
        return $"{WX.env.USER_DATA_PATH}" + "/AB/" + fileName;
#else
        return Application.streamingAssetsPath + "/AB/" + fileName;
        
#endif
    }
    /// <summary>
    /// 加载主包
    /// </summary>
    void LoadMainAB()
    {
        //加载清单文件AB包
        if (mainFile != null)
        {
            return;
        }
        mainFile = AssetBundle.LoadFromFile(GetABPath(MainABName));
        //获取清单信息
        manifestFile = mainFile.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
    }
    /// <summary>
    /// 判断目标包所需依赖是否加载
    /// </summary>
    /// <param name="abPath"></param>
    private void LoadDependent(string abPath)
    {
        string abMainifets = abPath + ".ab";
        //Debug.Log(abPath);
        LoadMainAB();
        //获取目标包的所有依赖包信息
        string[] relyOnABName = manifestFile.GetAllDependencies(abMainifets);
        for (int i = 0; i < relyOnABName.Length; i++)
        {
           
            //判断是否加载过该ab包
            if (!Instance.ABDic.ContainsKey(relyOnABName[i].Split('.')[0]))
            {
                //Debug.LogWarning("尚未加载"+relyOnABName[i].Split('.')[0]);
                //Debug.LogWarning("尚未加载"+ relyOnABName[i]);
                AssetBundle ab = AssetBundle.LoadFromFile(GetABPath(relyOnABName[i]));

                Instance.ABDic.Add(relyOnABName[i].Split('.')[0], ab);
            }
        }
        //加载目标AB包
        if (!Instance.ABDic.ContainsKey(abPath))
        {
            //Debug.Log("加载" + abPath+"中");
            //Debug.Log("路径：" + abMainifets + "中");
            AssetBundle targetAb = AssetBundle.LoadFromFile(GetABPath(abMainifets));
            Instance.ABDic.Add(abPath, targetAb);
        }
    }


    public string[] LoadAllDependent()
    {
        //主包
        LoadMainAB();
        string[] abs = manifestFile.GetAllAssetBundles();
        return abs;
       
    }
    /// <summary>
    /// 加载所有ab包资源
    /// </summary>
    private IEnumerator LoadAllABResource(string abName)
    {
        //加载清单文件AB包
        if (mainFile == null)
        {
            yield return new WaitForSeconds(0.1f);
            AssetBundleCreateRequest abcr = AssetBundle.LoadFromFileAsync(GetABPath(MainABName));
            yield return abcr;
            mainFile = abcr.assetBundle;
            //获取清单信息
            AssetBundleRequest abr = mainFile.LoadAssetAsync<AssetBundleManifest>("AssetBundleManifest");
            yield return abr;
            manifestFile = abr.asset as AssetBundleManifest;
        }
        //获取目标包的所有依赖包信息
        string[] relyOnABName = manifestFile.GetAllDependencies(abName);
        for (int i = 0; i < relyOnABName.Length; i++)
        {
            //判断是否加载过该ab包
            if (!ABDic.ContainsKey(relyOnABName[i]))
            {
                AssetBundleCreateRequest abcr = AssetBundle.LoadFromFileAsync(GetABPath(relyOnABName[i]) + ".ab");
                yield return abcr;
                AssetBundle ab = abcr.assetBundle;
                ABDic.Add(relyOnABName[i], ab);
            }
        }
        //加载目标AB包
        if (!ABDic.ContainsKey(abName))
        {
            AssetBundleCreateRequest abcr = AssetBundle.LoadFromFileAsync(GetABPath(abName) + ".ab");
            yield return abcr;
            AssetBundle targetAb = abcr.assetBundle;
            ABDic.Add(abName, targetAb);
        }
        yield return null;
    }


    #region 同步加载

    /// <summary>
    /// 同步加载AB包
    /// </summary>
    /// <param name="abPath"></param>
    /// <param name="resName"></param>
    /// <returns></returns>
    public Object LoadResource(string abPath,string resName)
    {
        LoadDependent(abPath);
        //获取资源
        Object obj = ABDic[abPath].LoadAsset(resName);
        if (obj == null)
        {
            Debug.LogError("在"+abPath+"ab包中找不到物体：" + resName);
            return null;
        }
        if (obj is GameObject)
            return Instantiate(obj) as GameObject;
    
        else
            return obj;
    }

    /// <summary>
    /// 同步加载AB包
    /// (用于热更新，lua不支持泛型）
    /// </summary>
    /// <param name="abName"></param>
    /// <param name="resName"></param>
    /// <returns></returns>
    public object LoadResource(string abName, string resName,System.Type type)
    {
        LoadDependent(abName);
        //获取资源
        Object obj = ABDic[abName].LoadAsset(resName,type);
        if (obj == null)
        {
            Debug.LogError("在" + abName + "ab包中找不到物体：" + resName);
            return null;
        }
        if (obj is GameObject)
            return GameObject.Instantiate(obj) as GameObject;
        else
            return obj;
    }

    /// <summary>
    /// 同步加载AB包
    /// (根据泛型）
    /// </summary>
    /// <param name="abName"></param>
    /// <param name="resName"></param>
    /// <returns></returns>
    public T LoadResource<T>(string abName, string resName) where T:Object
    {

#if UNITY_EDITOR
        string suffix;
        System.Type type = typeof(T);
        if (type == typeof(GameObject))
            suffix = ".prefab";
        else if (type == typeof(Sprite) || type == typeof(Texture2D))
            suffix = ".png"; // Texture 和 Sprite 都用 .png 后缀，可以根据需求调整
        else if (type == typeof(AudioClip))
            suffix = ".mp3";  
        else if (type == typeof(RuntimeAnimatorController))
            suffix = ".controller";  
        else
        {
            suffix = "";
            Debug.LogError("出现了意料之外的类型：" + type.Name);
        }
        var path = "Assets/abRes/Assetbundle/" + abName +"/"+ resName + suffix;

        //Debug.Log(path);
        return UnityEditor.AssetDatabase.LoadAssetAtPath<T>(path);


#else
#if UNITY_WEBGL
        //无需加载dependent，直接获取资源
        T obj = ABDic[abName].LoadAsset<T>(resName);
        System.Type type = typeof(T);
        if (obj == null)
        {
            Debug.LogError("在" + abName + "ab包中找不到物体：" + resName);
            return null;
        }
            return obj;
    #else
        LoadDependent(abName);
        //获取资源
        T obj = ABDic[abName].LoadAsset<T>(resName);
        System.Type type = typeof(T);
        if (obj == null)
        {
            Debug.LogError("在" + abName + "ab包中找不到物体：" + resName);
            return null;
        }
            return obj;
    #endif
#endif
    }


    /// <summary>
    /// 加载图片
    /// </summary>
    /// <param name="spriteName"></param>
    /// <returns></returns>
    public AudioClip LoadAudioClip(string abName,string audioName)
    {
        LoadDependent(abName);
        //获取资源
        AudioClip obj = ABDic[abName].LoadAsset<AudioClip>(audioName);
        if (obj == null)
        {
            Debug.LogError("在"+abName+"包中找不到物体：" + audioName);
            return null;
        }
        return obj;
    }

    /// <summary>
    /// 加载图片
    /// </summary>
    /// <param name="spriteName"></param>
    /// <returns></returns>
    public Sprite LoadSprite(string abName, string spriteName)
    {
        LoadDependent(abName);
        //获取资源
        Sprite obj = ABDic[abName].LoadAsset<Sprite>(spriteName);
        if (obj == null)
        {
            Debug.LogError("在"+abName+"包中找不到物体：" + spriteName);
            return null;
        }
        return obj;
    }

#endregion


#region 异步加载
    
/// <summary>
/// 加载主包（异步）
/// </summary>
private IEnumerator LoadMainABAsync()
{
    //加载清单文件AB包
    if (mainFile != null)
    {
        yield break;
    }
    AssetBundleCreateRequest abcr = AssetBundle.LoadFromFileAsync(GetABPath(MainABName));
    yield return abcr;
    if (abcr.assetBundle == null)
    {
        Debug.LogError("加载主包失败");
        yield break;
    }
    
    mainFile = abcr.assetBundle;
    manifestFile = mainFile.LoadAsset<AssetBundleManifest>("AssetBundleManifest");

    if (manifestFile == null)
    {
        Debug.LogError("加载清单文件失败");
    }
    //获取清单信息
    //manifestFile = mainFile.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
}
/// <summary>
/// 判断目标包所需依赖是否加载(异步）
/// </summary>
/// <param name="abPath"></param>
private IEnumerator LoadDependentAsync(string abPath)
{
    string abMainifets = abPath + ".ab";
    //Debug.Log(abPath);
    yield return StartCoroutine(LoadMainABAsync()) ;
    //获取目标包的所有依赖包信息
    string[] relyOnABName = manifestFile.GetAllDependencies(abMainifets);
    foreach (var dependency in relyOnABName)
    {
        //判断是否加载过该ab包
        if (!Instance.ABDic.ContainsKey(dependency.Split('.')[0]))
        {
            string dependencyName = dependency.Split('.')[0];
            //Debug.LogWarning("尚未加载"+relyOnABName[i].Split('.')[0]);
            //Debug.LogWarning("尚未加载"+ relyOnABName[i]);
            AssetBundleCreateRequest  abcr = AssetBundle.LoadFromFileAsync(GetABPath(dependencyName));
            yield return abcr;
            if (abcr.assetBundle == null)
            {
                Debug.LogError($"依赖包加载失败: {dependency}");
            }
            else
            {
                Instance.ABDic.Add(dependencyName, abcr.assetBundle);
            }
        }
    }
    //加载目标AB包
    if (!Instance.ABDic.ContainsKey(abPath))
    {
        AssetBundleCreateRequest abcr = AssetBundle.LoadFromFileAsync(GetABPath(abMainifets));
        yield return abcr;
        if (abcr.assetBundle == null)
        {
            Debug.LogError($"目标包加载失败: {abPath}");
        }
        else
        {
            Instance.ABDic.Add(abPath, abcr.assetBundle);
        }
        //Debug.Log("加载" + abPath+"中");
        //Debug.Log("路径：" + abMainifets + "中");
        AssetBundle targetAb = AssetBundle.LoadFromFile(GetABPath(abMainifets));
        Instance.ABDic.Add(abPath, targetAb);
    }
}
    
    //当前异步加载为  从AB包中异步加载资源
    //为完善 异步加载AB包

    /// <summary>
    /// 异步加载资源
    /// </summary>
    /// <param name="abName"></param>
    /// <param name="resName"></param>
    /// <param name="callBack"></param>
    public void LoadResourceAsync(string abName, string resName, UnityAction<Object> callBack=null)
    {
        StartCoroutine(IE_LoadResourceAsync(abName, resName, callBack));
    }
    private IEnumerator IE_LoadResourceAsync(string abName, string resName, UnityAction<Object> callBack=null)
    {
        yield return StartCoroutine(LoadDependentAsync(abName));
        //yield return StartCoroutine(LoadAllABResource(abName));
        //获取资源
        AssetBundleRequest abr = ABDic[abName].LoadAssetAsync(resName);
        yield return abr;
        if (abr.asset==null)
        {
            Debug.LogError("加载资源为空");
        }
        
        callBack?.Invoke(abr.asset);
        yield return null;
    }

    /// <summary>
    /// 异步加载资源（根据type）
    /// </summary>
    /// <param name="abName"></param>
    /// <param name="resName"></param>
    /// <param name="callBack"></param>
    public void LoadResourceAsync(string abName, string resName,System.Type type, UnityAction<Object> callBack)
    {
        StartCoroutine(IE_LoadResourceAsync(abName, resName, type, callBack));
    }
    private IEnumerator IE_LoadResourceAsync(string abName, string resName, System.Type type, UnityAction<Object> callBack)
    {
        //LoadDependent(abName);
        yield return StartCoroutine(LoadAllABResource(abName));
        //获取资源
        AssetBundleRequest abr = ABDic[abName].LoadAssetAsync(resName, type);
        yield return abr;
        if (abr.asset == null)
        {
            Debug.LogError("加载资源为空");
        }

        if (abr.asset is GameObject)
            callBack(Instantiate(abr.asset) as GameObject);
        else
            callBack(abr.asset);
        yield return null;
    }
    /// <summary>
    /// 异步加载资源（根据泛型）
    /// </summary>
    /// <param name="abName"></param>
    /// <param name="resName"></param>
    /// <param name="callBack"></param>
    public void LoadResourceAsync<T>(string abName, string resName, UnityAction<T> callBack) where T: Object
    {
        StartCoroutine(IE_LoadResourceAsync<T>(abName, resName, callBack));
    }
    private IEnumerator IE_LoadResourceAsync<T>(string abName, string resName, UnityAction<T> callBack) where T : Object
    {
        //LoadDependent(abName);
        yield return StartCoroutine(LoadAllABResource(abName));
        //获取资源
        AssetBundleRequest abr = ABDic[abName].LoadAssetAsync<T>(resName);
        yield return abr;
        if (abr.asset == null)
        {
            Debug.LogError("加载资源为空");
        }

        if (abr.asset is GameObject)
            callBack(Instantiate(abr.asset) as T);
        else
            callBack(abr.asset as T);
        yield return null;
    }


#endregion


    #region AB包卸载

    /// <summary>
    /// 卸载单个包
    /// </summary>
    /// <param name="abName"></param>
    public void UnloadABResource(string abName)
    {
        if (ABDic.ContainsKey(abName))
        {
            //此处参数意思个人理解为：是否影响到已经在场景中存在的物体
            ABDic[abName].Unload(false);
            ABDic.Remove(abName);
        }
    }

    /// <summary>
    /// 卸载所有ab资源
    /// </summary>
    public void UnloadAllABResource()
    {
        //此处参数与卸载单个包参数同理
        AssetBundle.UnloadAllAssetBundles(false);
        ABDic.Clear();
        //全部清空
        mainFile = null;
        manifestFile = null;
    }

#endregion
}
 